﻿#include <QCoreApplication>
#include <QMutex>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QDomDocument>
#include <QXmlStreamWriter>
#include <QSettings>
#include <QColor>
#include "Configuration.h"
#include "Constants.h"

namespace Domy {

Configuration::Configuration()
    : m_settings(Q_NULLPTR)
{
    m_recent_files = loadRecentFilesList();

    initSettings();
    initScheme();
}

Configuration::~Configuration()
{
    if (m_settings) {
        delete m_settings;
        m_settings = Q_NULLPTR;
    }
}

Configuration *Configuration::getInstance()
{
    static Configuration *s_instance = Q_NULLPTR;
    static QMutex s_mutex;
    QMutexLocker locker(&s_mutex);
    if (!s_instance) {
        s_instance = new Configuration();
    }
    return s_instance;
}

bool Configuration::saveWindowState(const QByteArray &data)
{
    QString file_name = QString("%1/%2").arg(qApp->applicationDirPath()).arg(WindowStateConfigurationFileName);
    return this->saveByteArrayFile(data, file_name);
}

bool Configuration::saveWindowState(const QByteArray &data, const QString &file_name)
{
    return this->saveByteArrayFile(data, file_name);
}

QByteArray Configuration::loadWindowState() const
{
    QString file_name = QString("%1/%2").arg(qApp->applicationDirPath()).arg(WindowStateConfigurationFileName);
    return this->loadByteArrayFile(file_name);
}

QByteArray Configuration::loadWindowState(const QString &file_name) const
{
    return this->loadByteArrayFile(file_name);
}

bool Configuration::saveWindowGeometry(const QByteArray &data)
{
    QString file_name = QString("%1/%2").arg(qApp->applicationDirPath()).arg(WindowGeometryConfigurationFileName);
    return this->saveByteArrayFile(data, file_name);
}

bool Configuration::saveWindowGeometry(const QByteArray &data, const QString &file_name)
{
    return this->saveByteArrayFile(data, file_name);
}

QByteArray Configuration::loadWindowGeometry() const
{
    QString file_name = QString("%1/%2").arg(qApp->applicationDirPath()).arg(WindowGeometryConfigurationFileName);
    return this->loadByteArrayFile(file_name);
}

QByteArray Configuration::loadWindowGeometry(const QString file_name) const
{
    return this->loadByteArrayFile(file_name);
}

QVariant Configuration::getSettingsValue(const QString &group, const QString &key) const
{
    return m_settings->value(QString("%1/%2").arg(group).arg(key));
}

void Configuration::setSettingsValue(const QString &group, const QString &key, const QVariant &value)
{
    m_settings->beginGroup(group);
    m_settings->setValue(key, value);
    m_settings->endGroup();
}

QStringList Configuration::getAllSchemeNames() const
{
    return m_scheme.keys();
}

QVariant Configuration::getColorSchemeValue(const QString &name, const QString &key) const
{
    QVariant value;
    if (m_scheme.contains(name)) {
        QSettings *settings = m_scheme.value(name);
        if (settings) {
            value = settings->value(QString("scheme/%1").arg(key));
        }
    }
    return value;
}

void Configuration::setColorSchemeValue(const QString &name, const QString &key, const QVariant &value) const
{
    if (m_scheme.contains(name)) {
        QSettings *settings = m_scheme.value(name);
        if (settings) {
            settings->setValue(QString("scheme/%1").arg(key), value);
        }
    }
}

bool Configuration::copyColorScheme(const QString &name, const QString &new_name)
{
    QString file_path = qApp->applicationDirPath() + "/" + SchemesDirName + "/" + name;
    QString new_file_path = qApp->applicationDirPath() + "/" + SchemesDirName + "/" + new_name;

    if (QFile::copy(file_path, new_file_path)) {
        QSettings *settings = new QSettings(new_file_path, QSettings::IniFormat);
        m_scheme[new_name] = settings;
        return true;
    }
    else {
        return false;
    }
}

bool Configuration::deleteColorScheme(const QString &name)
{
    QString file_path = qApp->applicationDirPath() + "/" + SchemesDirName + "/" + name;
    if (QFile::remove(file_path)) {
        if (m_scheme.contains(name)) {
            QSettings *settings = m_scheme.take(name);
            delete settings;
            settings = Q_NULLPTR;
        }

        return true;
    }
    else {
        return false;
    }
}

bool Configuration::saveRecetFilesList()
{
    QFile file(qApp->applicationDirPath() + "/" + RecentConfigFileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        return false;
    }

    QXmlStreamWriter writer(&file);
    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement("recent");
    for (const QString &item : m_recent_files) {
        writer.writeStartElement("item");
        writer.writeAttribute("path", item);
        writer.writeEndElement();
    }
    writer.writeEndElement();
    writer.writeEndDocument();

    file.close();

    if (!m_recent_files.isEmpty()) {
        QFileInfo fi = QFileInfo(m_recent_files.first());
        m_last_open_dir = fi.dir().path();
    }

    return true;
}

QStringList Configuration::getRecentFilesList() const
{
    return m_recent_files;
}

QStringList &Configuration::getRecentFilesListRef()
{
    return m_recent_files;
}

void Configuration::checkRecentFilesCounts()
{
    int max_cnts = this->getSettingsValue(SettingsGroup_General, SettingsKey_RecentFilesMaxCounts).toInt();
    while (m_recent_files.count() > max_cnts) {
        m_recent_files.removeLast();
    }
}

QString Configuration::getLastOpenDirPath() const
{
    if (m_last_open_dir.isEmpty()) {
        m_last_open_dir = qApp->applicationDirPath();
    }
    return m_last_open_dir;
}

bool Configuration::saveByteArrayFile(const QByteArray &data, const QString &file_name)
{
    QFile file(file_name);
    if (!file.open(QIODevice::Truncate | QIODevice::WriteOnly)) {
        return false;
    }
    file.write(data);
    file.close();
    return true;
}

QByteArray Configuration::loadByteArrayFile(const QString &file_name) const
{
    QByteArray data;
    QFile file(file_name);
    if (!file.open(QIODevice::ReadOnly)) {
        return data;
    }
    data = file.readAll();
    file.close();
    return data;
}

void Configuration::initSettings()
{
    QString settings_file_path = qApp->applicationDirPath() + "/" + SoftwareSettingsFileName;
    m_settings = new QSettings(settings_file_path, QSettings::IniFormat);

    QFileInfo fi(settings_file_path);
    if (!fi.exists()) {
        m_settings->beginGroup(SettingsGroup_General);
        m_settings->setValue(SettingsKey_RecentFilesMaxCounts, DefaultSettings_RecentFilesMaxCounts);
        m_settings->endGroup();

        m_settings->beginGroup(SettingsGroup_Editor);
        m_settings->setValue(SettingsKey_Family, DefaultSettings_FontFamily);
        m_settings->setValue(SettingsKey_FontSize, DefaultSettings_FontSize);
        m_settings->setValue(SettingsKey_SchemeName, DefaultSettings_SchemeName);
        m_settings->setValue(SettingsKey_TabPolicy, DefaultSettings_TabPolicy);
        m_settings->setValue(SettingsKey_TabSize, DefaultSettings_TabSize);
        m_settings->setValue(SettingsKey_Encoding, DefaultSettings_Encoding);
        m_settings->endGroup();

        m_settings->beginGroup(SettingsGroup_Workspace);
        m_settings->setValue(SettingsKey_WorkspaceDir, DefaultSettings_WorkspaceDir);
        m_settings->endGroup();

        m_settings->beginGroup(SettingsGroup_Message);
        m_settings->setValue(SettingsKey_MessageViewZoom, DefaultSettings_MessageViewZoom);
        m_settings->endGroup();

        m_settings->beginGroup(SettingsGroup_Issues);
        m_settings->setValue(SettingsKey_IssuesWarningVisible, true);
        m_settings->setValue(SettingsKey_IssuesErrorVisible, true);
        m_settings->endGroup();
    }
}

void Configuration::initScheme()
{
    QString dir_path = qApp->applicationDirPath() + "/" + SchemesDirName;
    QDir dir(dir_path);
    if (!dir.exists()) {
        dir.mkpath(dir_path);
    }

    QStringList files = dir.entryList(QDir::Files);

    // 内置light配色方案
    QString scheme_file_path = dir_path + "/" + SchemeName_Light;
    QSettings *settings = new QSettings(scheme_file_path, QSettings::IniFormat);

    if (!files.contains(SchemeName_Light)) {
        settings->beginGroup(SettingsGroup_Scheme);
        settings->setValue(SettingsKey_BGColor, QColor(Qt::white).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNTextColor, QColor(Qt::black).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNBGColor, QColor(240, 240, 240).name(QColor::HexRgb));
        settings->setValue(SettingsKey_CurrentLine, QColor(217, 235, 249).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Search, QColor(248, 201, 171).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Text, QStringList() << QColor(Qt::black).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Tag, QStringList() << QColor(128, 0, 0).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Attr, QStringList() << QColor(Qt::red).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_AttrText, QStringList() << QColor(Qt::blue).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Comment, QStringList() << QColor(0, 128, 0).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->endGroup();
    }

    m_scheme[SchemeName_Light] = settings;

    // 内置dark配色方案
    scheme_file_path = dir_path + "/" + SchemeName_Dark;
    settings = new QSettings(scheme_file_path, QSettings::IniFormat);

    if (!files.contains(SchemeName_Dark)) {
        settings->beginGroup(SettingsGroup_Scheme);
        settings->setValue(SettingsKey_BGColor, QColor(30, 30, 30).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNTextColor, QColor(Qt::white).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNBGColor, QColor(51, 51, 51).name(QColor::HexRgb));
        settings->setValue(SettingsKey_CurrentLine, QColor(51, 51, 51).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Search, QColor(97, 50, 20).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Text, QStringList() << QColor(Qt::white).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Tag, QStringList() << QColor(62, 156, 214).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Attr, QStringList() << QColor(156, 220, 254).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_AttrText, QStringList() << QColor(214, 157, 133).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Comment, QStringList() << QColor(87, 166, 74).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->endGroup();
    }

    m_scheme[SchemeName_Dark] = settings;

    // 内置origin配色方案
    scheme_file_path = dir_path + "/" + SchemeName_Origin;
    settings = new QSettings(scheme_file_path, QSettings::IniFormat);

    if (!files.contains(SchemeName_Origin)) {
        settings->beginGroup(SettingsGroup_Scheme);
        settings->setValue(SettingsKey_BGColor, QColor(Qt::white).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNTextColor, QColor(Qt::black).name(QColor::HexRgb));
        settings->setValue(SettingsKey_LNBGColor, QColor(Qt::lightGray).name(QColor::HexRgb));
        settings->setValue(SettingsKey_CurrentLine, QColor(217, 235, 249).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Search, QColor(248, 201, 171).name(QColor::HexRgb));
        settings->setValue(SettingsKey_Text, QStringList() << QColor(Qt::black).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Tag, QStringList() << QColor(Qt::darkBlue).name(QColor::HexRgb) << "true" << "false" << "false");
        settings->setValue(SettingsKey_Attr, QStringList() << QColor(Qt::darkGreen).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_AttrText, QStringList() << QColor(Qt::red).name(QColor::HexRgb) << "false" << "false" << "false");
        settings->setValue(SettingsKey_Comment, QStringList() << QColor(Qt::gray).name(QColor::HexRgb) << "false" << "true" << "false");
        settings->endGroup();
    }
    m_scheme[SchemeName_Origin] = settings;

    for (const QString &name : files) {
        if (name == SchemeName_Origin || name == SchemeName_Light
                || name == SchemeName_Dark) {
            continue;
        }
        QSettings *settings = new QSettings(dir_path + "/" + name, QSettings::IniFormat);
        m_scheme[name] = settings;
    }

    if (!m_scheme.contains(getSettingsValue(SettingsGroup_Editor, SettingsKey_SchemeName).toString())) {
        setSettingsValue(SettingsGroup_Editor, SettingsKey_SchemeName, SchemeName_Light);
    }
}

QStringList Configuration::loadRecentFilesList() const
{
    QStringList history;
    QFile file(qApp->applicationDirPath() + "/" + RecentConfigFileName);
    if (!file.open(QIODevice::ReadOnly)) {
        return history;
    }

    QDomDocument doc;
    if (!doc.setContent(&file)) {
        return history;
    }
    file.close();

    QDomElement element = doc.documentElement().firstChildElement();
    while (!element.isNull()) {

        history << element.attribute("path");

        element = element.nextSiblingElement();
    }

    if (history.isEmpty()) {
        m_last_open_dir = qApp->applicationDirPath();
    }
    else {
        QFileInfo fi = QFileInfo(history.first());
        m_last_open_dir = fi.dir().path();
    }

    return history;
}

} // namespace Domy
